package org.oschina.handler.lock;

import org.apache.commons.lang.math.RandomUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.oschina.handler.BaseAopHandler;
import org.oschina.handler.lock.annotation.Lock;
import org.oschina.helper.cache.ICacheHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

/**
 * 简易分布式锁
 */
@Component
@Aspect
public class LockHandler extends BaseAopHandler implements Ordered {

	private final static Logger logger=LoggerFactory.getLogger(LockHandler.class);
	
	@Autowired
	private ICacheHelper cacheHelper;
	private final static int WAIT_TIME=1000;

	//AOP执行顺序 越小越先执行
	@Override
	public int getOrder()
	{
		return 20;
	}
	
	@Pointcut("@annotation(org.oschina.handler.lock.annotation.Lock)")
	private void aroundPoint(){};
	
	@Around(value="aroundPoint()")
	private Object around(final ProceedingJoinPoint proceedingJoinPoint)
	{
		Lock lock=(Lock) getAnnotation(proceedingJoinPoint, Lock.class);
		
		return lock(lock.key(),lock.timeout(),new ILockHandler()
		{
			@Override
			public Object handle()
			{
				try
				{
					return proceedingJoinPoint.proceed();
				}
				catch(Throwable e)
				{
					logger.error("锁执行代码块异常 - ",e);
					return null;
				}
			}
		});
	}
	
	/**
	 * 锁
	 * @param key
	 * @param timeout
	 * @param handler
	 */
	public Object lock(String key,int timeout,ILockHandler handler)
	{
		int waitTime=RandomUtils.nextInt(WAIT_TIME);
		try
		{
			while(isLock(key))
			{
				Thread.sleep(waitTime);
			}
		}
		catch (InterruptedException e)
		{
			logger.error("锁等待异常 - ",e);
			return null;
		}
		
		lock(key,timeout);
		Object result=(handler!=null?handler.handle():null);
		unlock(key);
		return result;
	}
	
	/**
	 * 上锁
	 * @param key
	 * @param timeout
	 */
	private void lock(String key,int timeout)
	{
		cacheHelper.set(key, "true", timeout);
	}
	
	/**
	 * 移除锁
	 * @param key
	 */
	private void unlock(String key)
	{
		cacheHelper.del(key);
	}
	
	/**
	 * 是否锁住
	 * @return
	 */
	private boolean isLock(String key)
	{
		return cacheHelper.exists(key);
	}
}